﻿using System;
using System.Collections.Generic;
using Aludra.Core.Models;
using Aludra.Desktop.Core;

namespace Aludra.Core
{
    public enum GamePhase
    {
        Unspecified = 0,
        Draw = 1,
        Standby = 2,
        MainOne = 3,
        Battle = 4,
        MainTwo = 5,
        End = 6
    }

    public class CardGame
    {
        public const int DefaultLifePoint = 8000;
        public const int MinimumDeckSize = 5;

        #region Properties
        /// <summary>
        /// The initia life point. Default is 8000
        /// </summary>
        public int InitialLifePoint { get; set; }

        /// <summary>
        /// The player container
        /// </summary>
        public CardGamePlayer[] Players { get; private set; }

        private int _currentPlayerIndex;
        public int CurrentPlayerIndex
        {
            get { return _currentPlayerIndex; }
            protected set { _currentPlayerIndex = value; }
        }

        public int NextPlayerIndex
        {
            get
            {
                int index = (_currentPlayerIndex + 1) % this.Players.Length;
                while (index != _currentPlayerIndex)
                {
                    if (this.Players[index].Status == CardGamePlayerStatus.Unspecified)
                    {
                        return index;
                    }
                    index = (index + 1) % this.Players.Length;
                }
                return 0;
            }
        }

        public GamePhase CurrentPhase { get; protected set; }

        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="decks"></param>
        /// <exception cref="System.ArgumentException"></exception>
        public CardGame(IEnumerable<ICard>[] decks, IEnumerable<MonsterCard>[] extraDecks)
        {
            if (decks.Length < 2)
            {
                throw new ArgumentException("The number of initia decks must be greater than 1", "decks");
            }
            this.InitialLifePoint = DefaultLifePoint;
            this.Players = new CardGamePlayer[decks.Length];
            for (int i = 0; i < decks.Length; i++)
            {
                this.Players[i] = new CardGamePlayer(new DeckZone(decks[i]), new ExtraDeckZone(extraDecks[i]));
                if (this.Players[i].MyDeckZone.Count < MinimumDeckSize)
                {
                    throw new ArgumentException(string.Format("The deck must contains at least 5 cards.Player index is {0}", i));
                }
            }
        }

        /// <summary>
        /// Calling this method will start the game and the specified player will be moved to
        /// the draw phase
        /// </summary>
        /// <param name="playerIndex">The index of the player who performs the first attack</param>
        /// <param name="shuffle">Shuffles the deck of all players</param>
        /// <exception cref="System.ArgumentOutOfRangeException"></exception>
        public void Start(int playerIndex, bool shuffle)
        {
            if (playerIndex >= this.Players.Length || playerIndex < 0)
            {
                throw new ArgumentOutOfRangeException("playerIndex");
            }
            foreach (var player in this.Players)
            {
	            if (shuffle)
	            {
                	// Shuffles the cards in the deck zone
                    player.MyDeckZone.Shuffle();
                }
                player.LifePoint = this.InitialLifePoint;
                player.Status = CardGamePlayerStatus.Unspecified;
            	// Each player draws five cards
				var cardsDrawn = player.MyDeckZone.GetRange(0, MinimumDeckSize);
                player.MyDeckZone.RemoveRange(0, MinimumDeckSize);
				player.MyHandZone.AddRange(cardsDrawn);
            }
            _currentPlayerIndex = playerIndex;			
        }

        public bool MoveToPhase(GamePhase gamePhase)
        {
            if ((int)gamePhase <= (int)this.CurrentPhase
                || (this.CurrentPhase == GamePhase.MainOne && gamePhase == GamePhase.MainTwo))
            {
                return false;
            }
            switch (gamePhase)
            {
                case GamePhase.Draw: DrawPhase(); break;
                case GamePhase.Standby: break;
                case GamePhase.MainOne: break;
                case GamePhase.Battle: break;
                case GamePhase.MainTwo: break;
                case GamePhase.End: break;
                default: return false;
            }

            this.CurrentPhase = gamePhase;

            return true;
        }

        public void MoveToNextPhase()
        {
            GamePhase nextPhase = GamePhase.Unspecified;
            switch (this.CurrentPhase)
            {
                case GamePhase.Unspecified: nextPhase = GamePhase.Draw; break;
                case GamePhase.Draw: nextPhase = GamePhase.Standby; break;
                case GamePhase.Standby: nextPhase = GamePhase.MainOne; break;
                case GamePhase.MainOne: nextPhase = GamePhase.Battle; break;
                case GamePhase.Battle: nextPhase = GamePhase.MainTwo; break;
                case GamePhase.MainTwo: nextPhase = GamePhase.End; break;
                case GamePhase.End: EndPhase(); return;
            }
            MoveToPhase(nextPhase);
        }

        public void DrawCard(int count, int playerIndex)
        {
            var player = this.Players[playerIndex];
            int dCount = player.MyDeckZone.Count;
            if (dCount < count)
            {
                player.Status = CardGamePlayerStatus.Lost;
                return;
            }
			var cardsDrawn = player.MyDeckZone.GetRange(0, count);
			player.MyDeckZone.RemoveRange(0, count);
			player.MyHandZone.AddRange(cardsDrawn);
        }

        private void EndPhase()
        {
            this.CurrentPlayerIndex = this.NextPlayerIndex;
            this.CurrentPhase = GamePhase.Unspecified;
        }

        private void DrawPhase()
        {
            DrawCard(1, this.CurrentPlayerIndex);
        }
    }
}
